package com.scpii.api.controller.auth;

import java.security.Principal;
import java.util.HashMap;
import java.util.Map;

import javax.annotation.Resource;

import org.apache.commons.lang.StringUtils;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import com.scpii.api.common.Constants;
import com.scpii.api.common.annoation.ActionTypeMethod;
import com.scpii.api.common.auth.AuthorizationRequest;
import com.scpii.api.common.auth.AuthorizationRequestManager;
import com.scpii.api.common.auth.ClientAuthenticationDetails;
import com.scpii.api.common.auth.token.AuthAccessToken;
import com.scpii.api.common.auth.token.ResourceServerTokenServices;
import com.scpii.api.common.auth.token.ThirdPartyTokenGranter;
import com.scpii.api.common.auth.token.TokenGranter;
import com.scpii.api.common.base.Response;
import com.scpii.api.common.error.DefaultWebResponseExceptionTranslator;
import com.scpii.api.common.error.WebResponseExceptionTranslator;
import com.scpii.api.common.exception.ClientException;
import com.scpii.api.common.exception.ClientNotFoundException;
import com.scpii.api.common.exception.DotNotAllowRegisterException;
import com.scpii.api.common.exception.InvalidClientException;
import com.scpii.api.common.exception.UnSupportedGrantTypeException;
import com.scpii.api.domain.app.AppConfigs;
import com.scpii.api.domain.user.UserActions.ActionType;
import com.scpii.api.service.app.AppConfigsService;
import com.scpii.api.util.PrincipalUtil;
import com.scpii.api.util.RegisterVerifyUtil;

@Controller
@RequestMapping("/auth")
public class TokenController {

	private WebResponseExceptionTranslator providerExceptionHandler = new DefaultWebResponseExceptionTranslator();

	@Resource(name = "compositeTokenGranter")
	private TokenGranter tokenGranter;

	@Resource(name = "defaultAuthorizationRequestManager")
	private AuthorizationRequestManager authorizationRequestManager;

	@Resource
	private AppConfigsService appConfigsService;

	@Resource(name = "defaultTokenServices")
	private ResourceServerTokenServices resourceServerTokenServices;

	@RequestMapping(value = "/token", method = RequestMethod.POST)
	@ResponseBody
	@ActionTypeMethod(ActionType.USER_LOGIN)
	public AuthAccessToken getAccessToken(
			Principal principal,
			@RequestParam(value = "appId") String appId,
			@RequestParam(value = "grantType", defaultValue = "client", required = false) String grantType,
			@RequestParam(value = "loginType", defaultValue = "T_SINA", required = false) String loginType,
			@RequestParam(value = "password", defaultValue = StringUtils.EMPTY, required = false) String password,
			@RequestParam Map<String, String> parameters) {

		if (!(principal instanceof Authentication)) {
			throw new InsufficientAuthenticationException(
					"There is no client authentication");
		}
		Authentication client = (Authentication) principal;
		if (!client.isAuthenticated()) {
			throw new InsufficientAuthenticationException(
					"The client is not authenticated.");
		}

		AppConfigs appConfigs = appConfigsService.selectByAppIdAndKey(
				PrincipalUtil.getPrincipal().getAppId(),
				Constants.REGISTER_ALLOW_KEY);
		if (ThirdPartyTokenGranter.GRANT_TYPE.equals(grantType)
				&& !RegisterVerifyUtil.verifyThirdParty(loginType,
						appConfigs == null ? null : appConfigs.getValue())) {
			throw new DotNotAllowRegisterException();
		}

		HashMap<String, String> request = new HashMap<String, String>(
				parameters);
		request.put("appId", appId);
		request.put("loginType", loginType);
		request.put("password", password);
		AuthorizationRequest authorizationRequest = authorizationRequestManager
				.createAuthorizationRequest(request);

		AuthAccessToken token = tokenGranter.grant(grantType,
				authorizationRequest);
		if (token == null) {
			throw new UnSupportedGrantTypeException("Unsupported grant type: "
					+ grantType);
		}
		return token;
	}

	@RequestMapping(value = "/logout", method = RequestMethod.POST)
	@ResponseBody
	public Boolean logout() {
		ClientAuthenticationDetails details = (ClientAuthenticationDetails) SecurityContextHolder
				.getContext().getAuthentication().getDetails();
		return resourceServerTokenServices.revokeToken(details.getTokenValue());
	}

	@ExceptionHandler(ClientNotFoundException.class)
	public ResponseEntity<Response> handleNoSuchClientException(Exception e)
			throws Exception {
		return handleException(new InvalidClientException(e.getMessage(), e));
	}

	@ExceptionHandler(ClientException.class)
	public ResponseEntity<Response> handleException(Exception e)
			throws Exception {
		return providerExceptionHandler.translate(e);
	}

}
